// X-Chat TIGCC Doc search plugin by Raoul Prévost

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define CHEMIN_FICHIERS_AIDE "Index.hhk"
#define CHEMIN_FICHIERS_DATA "data.c"
#define LONGUEUR_LIGNE 2048

typedef struct {
	const char *titre;
	const char *url;
} Lien;

typedef struct {
	const char *mot;
	size_t nb_liens;
	const Lien *liens;
	int num;
} Cle;

void liberer_cles(Cle *cles, size_t taille_cles) {
	size_t i, j;
	for (i = 0; i < taille_cles; i++) {
		free((char*)cles[i].mot);
		for (j = 0; j < cles[i].nb_liens; j++) {
			free((char*)(cles[i].liens)[j].titre);
			free((char*)(cles[i].liens)[j].url);
		}
		free((Lien*)cles[i].liens);
	}
	free(cles);
}

char *decode_url(char *fichier) {
	char url[256];
	char *hlink = strchr(fichier, '_');
	char *phlink = strchr(fichier, '#');
	if (phlink) *phlink = '\0';
	char *extension = strrchr(fichier, '.');
	if (extension) *extension = '\0';
	if (hlink && (!phlink || hlink < phlink)) *hlink = '\0';
	if (phlink) hlink = phlink;
	
	strcpy(url, fichier);
	strcat(url, ".html");

	if (hlink) {
		*hlink = '#';
		strcat(url, hlink);
	}

	return strdup(url);
}

void decode_titre(char *titre, char *titre_decode) {
	char *ftitre = titre, *fstitre;
	*titre_decode = '\0';
	while ((fstitre = strchr(ftitre, '&'))) {
		*(fstitre++) = '\0';
		strcat(titre_decode, ftitre);
		if (!strncmp(fstitre, "lt;", 3)) {
			strcat(titre_decode, "<");
			fstitre += 3;
		} else if (!strncmp(fstitre, "gt;", 3)) {
			strcat(titre_decode, ">");
			fstitre += 3;
		} else if (!strncmp(fstitre, "amp;", 4)) {
			strcat(titre_decode, "&");
			fstitre += 4;
		} else if (!strncmp(fstitre, "quot;", 5)) {
			strcat(titre_decode, "\"");
			fstitre += 5;
		} else strcat(titre_decode, "&");
		ftitre = fstitre;
	}
	strcat(titre_decode, ftitre);
}

bool parse_sous_menu(FILE *hhk, Lien **liens, size_t *nb_liens) {
	char ligne[LONGUEUR_LIGNE], titre[256], titre_decode[256], fichier[256], *url;
	bool etat = false, present = false;
	size_t i;
	while (fgets(ligne, LONGUEUR_LIGNE - 1, hhk) && strncasecmp(ligne, "</UL>", 5)) {
		if (!etat) {
			if (sscanf(ligne, "<PARAM NAME=\"Name\" VALUE=\"%255[^\"]\">", titre)) etat = true;
		} else if (sscanf(ligne, "<PARAM NAME=\"Local\" VALUE=\"%255[^\"]\">", fichier)) {
			etat = false;
			present = false;
			url = decode_url(fichier);
			for (i = 0; i < *nb_liens && !present; i++) {
				present = !strcmp((*liens)[i].url, url);
			}
			if (!present) {
				Lien *nv_liens = realloc(*liens, (*nb_liens + 1) * sizeof(Lien));
				if (!nv_liens) {
					for (i = 0; i < *nb_liens; i++) {
						free((char*)(*liens)[i].titre);
						free((char*)(*liens)[i].url);
					}
					free(*liens);
					return false;
				}
				*liens = nv_liens;
				decode_titre(titre, titre_decode);
				(*liens)[*nb_liens].titre = strdup(titre_decode);
				(*liens)[*nb_liens].url = url;
				(*nb_liens)++;
			} else free(url);
		}
	}
	return true;
}

int clecmp(const void *cle1, const void *cle2) {
	return strcmp(((Cle*)cle1)->mot, ((Cle*)cle2)->mot);
}

int clecasecmp(const void *cle1, const void *cle2) {
	return strcasecmp(((Cle*)cle1)->mot, ((Cle*)cle2)->mot);
}

void fputs_addslashes(const char *str, FILE *f) {
	char *strd = strdup(str);
	char *antislash = strchr(strd, '\\');
	char *quot = strchr(strd, '"');
	char *fstr = strd, *fsstr, c;
	while (antislash || quot) {
		fsstr = antislash ? (quot ? ((antislash < quot) ? antislash : quot) : antislash) : quot;
		c = *fsstr;
		*fsstr = '\0';
		fputs(fstr, f);
		fputc('\\', f);
		fputc(c, f);
		fstr = fsstr + 1;
		antislash = strchr(fstr, '\\');
		quot = strchr(fstr, '"');
	}
	fputs(fstr, f);
	free(strd);
}

bool make_data_file(const char *source_hhk, const char *data_file) {
	FILE *hhk = fopen(source_hhk, "r");
	short etat = 0;
	char mot[256], titre[256], titre_decode[256], fichier[256], mot_decode[256];
	size_t nb_liens = 0;
	Lien *liens = NULL;
	size_t capacite_cles = 100;
	size_t taille_cles = 0;
	size_t level = 0;
	bool premier = true;
	Cle *cles = malloc(capacite_cles * sizeof(*cles));
	char ligne[LONGUEUR_LIGNE];
	if (hhk) {
		while (fgets(ligne, LONGUEUR_LIGNE - 1, hhk)) {
			if (!etat && sscanf(ligne, "<PARAM NAME=\"Name\" VALUE=\"%255[^\"]\">", mot)) {
				etat++;
				if (!premier) {
					if (taille_cles == capacite_cles) {
						capacite_cles += 100;
						Cle *nv_cles = realloc(cles, capacite_cles * sizeof(*nv_cles));
						if (!nv_cles) {
							liberer_cles(cles, taille_cles);
							fclose(hhk);
							return false;
						}
						cles = nv_cles;
					}
					cles[taille_cles].mot = strdup(mot_decode);
					cles[taille_cles].nb_liens = nb_liens;
					cles[taille_cles].liens = liens;
					cles[taille_cles].num = taille_cles;
					taille_cles++;
				}
			} else if (sscanf(ligne, "<PARAM NAME=\"Name\" VALUE=\"%255[^\"]\">", titre)) etat++;
			else if (sscanf(ligne, "<PARAM NAME=\"Local\" VALUE=\"%255[^\"]\">", fichier)) {
				etat = 0;
				premier = false;
				nb_liens = 0;
				liens = NULL;
				decode_titre(mot, mot_decode);
				liens = malloc(sizeof(Lien));
				if (!liens) {
					liberer_cles(cles, taille_cles);
					fclose(hhk);
					return false;
				}
				decode_titre(titre, titre_decode);
				liens->titre = strdup(titre_decode);
				liens->url = decode_url(fichier);
				nb_liens = 1;

			} else if (!etat && !strncasecmp(ligne, "<UL>", 4)) {
				if (level >= 1) {
					if (!parse_sous_menu(hhk, &liens, &nb_liens)) {
						liberer_cles(cles, taille_cles);
						fclose(hhk);
						return false;
					}
				} else level++;
			}
		}

		if (!premier) {
			cles[taille_cles].mot = strdup(mot_decode);
			cles[taille_cles].nb_liens = nb_liens;
			cles[taille_cles].liens = liens;
			cles[taille_cles].num = taille_cles;
			taille_cles++;
		}


		size_t i, j;
		fclose(hhk);

		FILE *data = fopen(data_file, "w");
		fputs("#include \"xcdoc.h\"\n\n", data);

		for (i = 0; i < taille_cles; i++) {
			fprintf(data, "const Lien xcdoc_liens_%d[] = {\n", cles[i].num);
			for (j = 0; j < cles[i].nb_liens; j++) {
				fputs("    {\"", data);
				fputs_addslashes((cles[i].liens)[j].titre, data);
				fputs("\", \"", data);
				fputs_addslashes((cles[i].liens)[j].url, data);
				fputs("\"}", data);
				if (j != cles[i].nb_liens - 1) fputc(',', data);
				fputc('\n', data);
			}
			fputs("};\n\n", data);
		}

		qsort(cles, taille_cles, sizeof(Cle), clecmp);

		fputs("const Cle xcdoc_cles[] = {\n", data);
		for (i = 0; i < taille_cles; i++) {
			fputs("    {\"", data);
			fputs_addslashes(cles[i].mot, data);
			fprintf(data, "\", %d, xcdoc_liens_%d}", cles[i].nb_liens, cles[i].num);
			if (i != taille_cles - 1) fputc(',', data);
			fputc('\n', data);
		}
		fputs("};\n\n", data);

		qsort(cles, taille_cles, sizeof(Cle), clecasecmp);

		fputs("const Cle xcdoc_case_cles[] = {\n", data);
		for (i = 0; i < taille_cles; i++) {
			fputs("    {\"", data);
			fputs_addslashes(cles[i].mot, data);
			fprintf(data, "\", %d, xcdoc_liens_%d}", cles[i].nb_liens, cles[i].num);
			if (i != taille_cles - 1) fputc(',', data);
			fputc('\n', data);
		}
		fprintf(data, "};\n\nconst size_t xcdoc_nb_cles = %d;\n\n", taille_cles);
		fclose(data);

		liberer_cles(cles, taille_cles);
		return true;
	}
	return false;
}


int main(void)
{
	if (!make_data_file(CHEMIN_FICHIERS_AIDE, CHEMIN_FICHIERS_DATA)) {
		fputs("error making data file\n", stderr);
		return 1;
	}
	return 0;
}
